using System;
using System.Collections.Generic;
using SimpleJSON;
using UnityEngine;

namespace Gj
{
    public class EventService : GameService
    {
        public static EventService single;
        private Dictionary<string, List<Event>> eventDic;

        static EventService()
        {
            single = new EventService();
        }

        public void OnDestroy()
        {
            single = new EventService();
        }

        public void OnInit()
        {
            if (eventDic == null) eventDic = new Dictionary<string, List<Event>>();
        }

        public void OnUpdate()
        {
            
        }

        private string On(string key, Event e)
        {
            var eventList = GetEventList(key);
            if (eventList == null)
            {
                eventList = new List<Event>();
                eventDic.Add(key, eventList);
            }

            eventList.Add(e);
            return e.key;
        }

        public string On(byte type, byte category, Action eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                action = eventAction,
                once = once
            };
            return On(GetKey(type, category), e);
        }

        public string On(byte type, Action<byte> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                cateAction = eventAction,
                once = once
            };
            return On(GetKey(type), e);
        }

        public string Once(byte type, Action<byte> eventAction)
        {
            return On(type, eventAction, true);
        }

        public string Once(byte type, byte category, Action eventAction)
        {
            return On(type, category, eventAction, true);
        }

        public string On(byte type, byte category, Action<object> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                actionObject = eventAction,
                once = once
            };
            return On(GetKey(type, category), e);
        }

        public string On(byte type, Action<byte, object> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                cateActionObject = eventAction,
                once = once
            };
            return On(GetKey(type), e);
        }

        public string Once(byte type, Action<byte, object> eventAction)
        {
            return On(type, eventAction, true);
        }

        public string Once(byte type, byte category, Action<object> eventAction)
        {
            return On(type, category, eventAction, true);
        }

        public string On(byte type, byte category, Action<string> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                actionString = eventAction,
                once = once
            };
            return On(GetKey(type, category), e);
        }

        public string On(byte type, Action<byte, string> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                cateActionString = eventAction,
                once = once
            };
            return On(GetKey(type), e);
        }

        public string Once(byte type, Action<byte, string> eventAction)
        {
            return On(type, eventAction, true);
        }

        public string Once(byte type, byte category, Action<string> eventAction)
        {
            return On(type, category, eventAction, true);
        }

        public string On(byte type, byte category, Action<float> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                actionFloat = eventAction,
                once = once
            };
            return On(GetKey(type, category), e);
        }

        public string On(byte type, Action<byte, float> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                cateActionFloat = eventAction,
                once = once
            };
            return On(GetKey(type), e);
        }

        public string Once(byte type, Action<byte, float> eventAction)
        {
            return On(type, eventAction, true);
        }

        public string Once(byte type, byte category, Action<float> eventAction)
        {
            return On(type, category, eventAction, true);
        }

        public string On(byte type, byte category, Action<int> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                actionInt = eventAction,
                once = once
            };
            return On(GetKey(type, category), e);
        }

        public string On(byte type, Action<byte, int> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                cateActionInt = eventAction,
                once = once
            };
            return On(GetKey(type), e);
        }

        public string Once(byte type, Action<byte, int> eventAction)
        {
            return On(type, eventAction, true);
        }

        public string Once(byte type, byte category, Action<int> eventAction)
        {
            return On(type, category, eventAction, true);
        }

        public string On(byte type, byte category, Action<bool> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                actionBool = eventAction,
                once = once
            };
            return On(GetKey(type, category), e);
        }

        public string On(byte type, Action<byte, bool> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                cateActionBool = eventAction,
                once = once
            };
            return On(GetKey(type), e);
        }

        public string Once(byte type, Action<byte, bool> eventAction)
        {
            return On(type, eventAction, true);
        }

        public string Once(byte type, byte category, Action<bool> eventAction)
        {
            return On(type, category, eventAction, true);
        }

        public string On(byte type, byte category, Action<JSONObject> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                actionObj = eventAction,
                once = once
            };
            return On(GetKey(type, category), e);
        }

        public string On(byte type, Action<byte, JSONObject> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                cateActionObj = eventAction,
                once = once
            };
            return On(GetKey(type), e);
        }

        public string Once(byte type, Action<byte, JSONObject> eventAction)
        {
            return On(type, eventAction, true);
        }

        public string Once(byte type, byte category, Action<JSONObject> eventAction)
        {
            return On(type, category, eventAction, true);
        }

        public string On(byte type, byte category, Action<JSONArray> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                actionArr = eventAction,
                once = once
            };
            return On(GetKey(type, category), e);
        }

        public string On(byte type, Action<byte, JSONArray> eventAction, bool once = false)
        {
            var e = new Event
            {
                key = StringTools.GenerateStr(8),
                cateActionArr = eventAction,
                once = once
            };
            return On(GetKey(type), e);
        }

        public string Once(byte type, Action<byte, JSONArray> eventAction)
        {
            return On(type, eventAction, true);
        }

        public string Once(byte type, byte category, Action<JSONArray> eventAction)
        {
            return On(type, category, eventAction, true);
        }

        public void Off(byte type, string key)
        {
            var eventList = GetEventList(GetKey(type));
            if (eventList != null)
                foreach (var e in eventList)
                    if (key.Equals(e.key))
                        eventList.Remove(e);
        }

        public void Off(byte type, byte category, string key)
        {
            var eventList = GetEventList(GetKey(type, category));
            if (eventList != null)
                foreach (var e in eventList)
                    if (key.Equals(e.key))
                        eventList.Remove(e);
        }

        private List<Event> GetEventList(string key)
        {
            if (eventDic == null) eventDic = new Dictionary<string, List<Event>>();
            if (eventDic.ContainsKey(key))
                return eventDic[key];
            return null;
        }

        private void HandleEvent(byte type, byte category, Action<Event> action)
        {
            if (eventDic == null) return;
            var eventList = GetEventList(GetKey(type));
            if (eventList != null)
                foreach (var e in eventList)
                {
                    action(e);
                    if (e.once) eventList.Remove(e);
                }

            if (category != 0)
            {
                eventList = GetEventList(GetKey(type, category));
                if (eventList != null)
                    foreach (var e in eventList)
                    {
                        action(e);
                        if (e.once) eventList.Remove(e);
                    }
            }
        }

        public void Emit(byte type)
        {
            Emit(type, 0);
        }

        public void Emit(byte type, byte category)
        {
            HandleEvent(type, category, e =>
            {
                if (e.action != null) e.action();
                if (e.cateAction != null) e.cateAction(category);
            });
        }

        public void Emit(byte type, object value)
        {
            Emit(type, 0, value);
        }

        public void Emit(byte type, byte category, object value)
        {
            HandleEvent(type, category, e =>
            {
                if (e.actionObject != null) e.actionObject(value);
                if (e.cateActionString != null) e.cateActionObject(category, value);
            });
        }

        public void Emit(byte type, string value)
        {
            Emit(type, 0, value);
        }

        public void Emit(byte type, byte category, string value)
        {
            HandleEvent(type, category, e =>
            {
                if (e.actionString != null) e.actionString(value);
                if (e.cateActionString != null) e.cateActionString(category, value);
            });
        }

        public void Emit(byte type, float value)
        {
            Emit(type, 0, value);
        }

        public void Emit(byte type, byte category, float value)
        {
            HandleEvent(type, category, e =>
            {
                if (e.actionFloat != null) e.actionFloat(value);
                if (e.cateActionFloat != null) e.cateActionFloat(category, value);
            });
        }

        public void Emit(byte type, int value)
        {
            Emit(type, 0, value);
        }

        public void Emit(byte type, byte category, int value)
        {
            HandleEvent(type, category, e =>
            {
                if (e.actionInt != null) e.actionInt(value);
                if (e.cateActionInt != null) e.cateActionInt(category, value);
            });
        }

        public void Emit(byte type, bool value)
        {
            Emit(type, 0, value);
        }

        public void Emit(byte type, byte category, bool value)
        {
            HandleEvent(type, category, e =>
            {
                if (e.actionBool != null) e.actionBool(value);
                if (e.cateActionBool != null) e.cateActionBool(category, value);
            });
        }

        public void Emit(byte type, JSONObject value)
        {
            Emit(type, 0, value);
        }

        public void Emit(byte type, byte category, JSONObject value)
        {
            HandleEvent(type, category, e =>
            {
                if (e.actionObj != null) e.actionObj(value);
                if (e.cateActionObj != null) e.cateActionObj(category, value);
            });
        }

        public void Emit(byte type, JSONArray value)
        {
            Emit(type, 0, value);
        }

        public void Emit(byte type, byte category, JSONArray value)
        {
            HandleEvent(type, category, e =>
            {
                if (e.actionArr != null) e.actionArr(value);
                if (e.cateActionArr != null) e.cateActionArr(category, value);
            });
        }

        private string GetKey(byte type, byte category)
        {
            return type + "-" + category;
        }

        private string GetKey(byte type)
        {
            return type + "*";
        }

        private struct Event
        {
            public Action action;
            public Action<byte> cateAction;
            public Action<object> actionObject;
            public Action<byte, object> cateActionObject;
            public Action<string> actionString;
            public Action<byte, string> cateActionString;
            public Action<float> actionFloat;
            public Action<byte, float> cateActionFloat;
            public Action<int> actionInt;
            public Action<byte, int> cateActionInt;
            public Action<bool> actionBool;
            public Action<byte, bool> cateActionBool;
            public Action<JSONObject> actionObj;
            public Action<byte, JSONObject> cateActionObj;
            public Action<JSONArray> actionArr;
            public Action<byte, JSONArray> cateActionArr;
            public string key;
            public bool once;
        }
    }
}